<!DOCTYPE html>
<html>
<head>
    <title>Simple Harmonic Motion Graphs</title>
</head>
<body>
    <p>
        此图为两个简谐振动的 x-t 图像。下面是根据图像得出的简谐振动方程的分析过程和使用 Canvas 绘制的图像。
    </p>
    <p>
        <strong>对图(a)的分析:</strong>
        <ol>
            <li><strong>振幅 (A):</strong> 从图中可以看出，振动的最大位移是 10 cm，所以振幅 A = 10 cm。</li>
            <li><strong>周期 (T):</strong> 图像显示，振子从平衡位置开始，经过一个完整的振动（到达正向最大位移，再到负向最大位移，最后回到平衡位置）所用的时间是 2 s。所以周期 T = 2 s。</li>
            <li><strong>角频率 (ω):</strong> 角频率与周期的关系是 ω = 2π/T。因此，ω = 2π/2 = π rad/s。</li>
            <li><strong>初相位 (φ):</strong> 振动在 t=0 时刻从平衡位置 (x=0) 开始，并向正方向运动（初始斜率为正）。这对应于一个没有相位平移的正弦函数。在一般形式 x = A sin(ωt + φ) 中，当 t=0 时 x=0，意味着 sin(φ)=0。同时，初速度 v(0) > 0，而 v = Aω cos(ωt + φ)，所以 v(0) = Aω cos(φ) > 0，这要求 cos(φ) > 0。因此，初相位 φ = 0。</li>
        </ol>
        综上，图(a)的简谐振动方程为： <strong>x = 10sin(πt) (cm)</strong>。
    </p>
    <p>
        <strong>对图(b)的分析:</strong>
        <ol>
            <li><strong>振幅 (A):</strong> 图中显示的最大位移是 10 cm，所以振幅 A = 10 cm。</li>
            <li><strong>周期 (T):</strong> 振动的形态与图(a)相似，可以推断其周期也为 T = 2 s。</li>
            <li><strong>角频率 (ω):</strong> ω = 2π/T = π rad/s。</li>
            <li><strong>初相位 (φ):</strong> 在 t=0 时刻，位移 x = 5 cm。代入 x = A sin(ωt + φ)，我们得到 5 = 10 sin(0 + φ)，所以 sin(φ) = 0.5。可能的解是 φ = π/6 或 φ = 5π/6。为了确定初相位，我们观察初始速度。在 t=0 时，曲线的斜率为正，表示初速度 v(0) > 0。速度的表达式是 v = Aω cos(ωt + φ)。在 t=0 时，v(0) = Aω cos(φ) > 0，要求 cos(φ) > 0。在两个可能的解中，只有当 φ = π/6 时，cos(π/6) = √3/2 > 0，符合条件。因此，初相位 φ = π/6。</li>
        </ol>
        综上，图(b)的简谐振动方程为： <strong>x = 10sin(πt + π/6) (cm)</strong>。
    </p>

    <canvas id="shmCanvas" width="700" height="350"></canvas>

    <script>
        const canvas = document.getElementById('shmCanvas');
        const ctx = canvas.getContext('2d');

        // ---通用绘图函数---
        function drawArrow(ctx, fromx, fromy, tox, toy, text) {
            const headlen = 10; // length of head in pixels
            const dx = tox - fromx;
            const dy = toy - fromy;
            const angle = Math.atan2(dy, dx);
            ctx.beginPath();
            ctx.moveTo(fromx, fromy);
            ctx.lineTo(tox, toy);
            ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
            ctx.moveTo(tox, toy);
            ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
            ctx.stroke();
        }

        function drawAxes(ctx, originX, originY, width, height, xLabel, yLabel) {
            ctx.strokeStyle = '#000';
            ctx.fillStyle = '#000';
            ctx.lineWidth = 1.5;
            ctx.font = '16px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';

            // Draw axes with arrows
            drawArrow(ctx, originX - width*0.1, originY, originX + width, originY);
            drawArrow(ctx, originX, originY + height, originX, originY - height - 20);
            
            // Axes labels
            ctx.fillText(xLabel, originX + width, originY + 20);
            ctx.fillText(yLabel, originX, originY - height - 30);
            ctx.fillText('O', originX - 12, originY + 12);
        }

        // ---绘图参数---
        const xScale = 60; // 像素每秒
        const yScale = 10; // 像素每厘米

        // ---绘制图 (a)---
        {
            const originX = 100;
            const originY = 175;
            const axisWidth = 2.5 * xScale;
            const axisHeight = 11 * yScale;

            drawAxes(ctx, originX, originY, axisWidth, axisHeight, 't/s', 'x/cm');
            
            // Ticks and labels for (a)
            ctx.fillText('1', originX + 1 * xScale, originY + 15);
            ctx.fillText('2', originX + 2 * xScale, originY + 15);
            ctx.textAlign = 'right';
            ctx.fillText('10', originX - 8, originY - 10 * yScale);
            ctx.fillText('-10', originX - 8, originY + 10 * yScale);

            // Dashed lines for amplitude
            ctx.setLineDash([5, 3]);
            ctx.beginPath();
            ctx.moveTo(originX, originY - 10 * yScale);
            ctx.lineTo(originX + 0.5 * xScale, originY - 10 * yScale);
            ctx.moveTo(originX, originY + 10 * yScale);
            ctx.lineTo(originX + 1.5 * xScale, originY + 10 * yScale);
            ctx.stroke();
            ctx.setLineDash([]);
            
            // Draw curve for (a): x = 10 * sin(pi*t)
            ctx.beginPath();
            ctx.lineWidth = 2;
            ctx.strokeStyle = '#000000';
            const tMaxA = 2.3;
            for (let t = 0; t <= tMaxA; t += 0.01) {
                const x = 10 * Math.sin(Math.PI * t);
                const canvasX = originX + t * xScale;
                const canvasY = originY - x * yScale;
                if (t === 0) {
                    ctx.moveTo(canvasX, canvasY);
                } else {
                    ctx.lineTo(canvasX, canvasY);
                }
            }
            ctx.stroke();
            
            // Label (a)
            ctx.textAlign = 'center';
            ctx.fillText('(a)', originX + 1 * xScale, originY + axisHeight + 20);
        }
        
        // ---绘制图 (b)---
        {
            const originX = 450;
            const originY = 175;
            const axisWidth = 2.0 * xScale;
            const axisHeight = 11 * yScale;
            
            drawAxes(ctx, originX, originY, axisWidth, axisHeight, 't/s', 'x/cm');

            // Ticks and labels for (b)
            ctx.fillText('1', originX + 1 * xScale, originY + 15);
            ctx.textAlign = 'right';
            ctx.fillText('10', originX - 8, originY - 10 * yScale);
            ctx.fillText('5', originX - 8, originY - 5 * yScale);
            ctx.fillText('-10', originX - 8, originY + 10 * yScale);

            // Dashed line for amplitude
            ctx.setLineDash([5, 3]);
            ctx.beginPath();
            ctx.moveTo(originX, originY - 10 * yScale);
            ctx.lineTo(originX + (1/3) * xScale, originY - 10 * yScale);
            ctx.moveTo(originX, originY + 10 * yScale);
            ctx.lineTo(originX + (5/6+1) * xScale, originY + 10 * yScale);
            ctx.stroke();
            ctx.setLineDash([]);

            // Draw curve for (b): x = 10 * sin(pi*t + pi/6)
            ctx.beginPath();
            ctx.lineWidth = 2;
            ctx.strokeStyle = '#000000';
            const phi = Math.PI / 6;
            const tMaxB = 1.8;
            for (let t = 0; t <= tMaxB; t += 0.01) {
                const x = 10 * Math.sin(Math.PI * t + phi);
                const canvasX = originX + t * xScale;
                const canvasY = originY - x * yScale;
                if (t === 0) {
                    ctx.moveTo(canvasX, canvasY);
                } else {
                    ctx.lineTo(canvasX, canvasY);
                }
            }
            ctx.stroke();

            // Label (b)
            ctx.textAlign = 'center';
            ctx.fillText('(b)', originX + 0.9 * xScale, originY + axisHeight + 20);
        }

    </script>
</body>
</html>